import serial
import time
import logging
import threading
import re

import serial.tools.list_ports

logging.basicConfig(level=logging.DEBUG, format='[%(asctime)s][%(name)s][%(levelname)s]  - %(message)s')
log = logging.getLogger("UART MOD")


COM_SYMBOL = "LED_MATRIX"

def search_com():
    ports = serial.tools.list_ports.comports()
    for i in ports:
        log.debug(f"port: {i.device} - {i.serial_number} - {i.manufacturer} - {i.product} - {i.description}")
        try:
            # set bit rate to 115200
            inst = serial.Serial(i.device, 115200, timeout=0.1)
            log.debug(f"checking: {i.device}")
            if check_com(inst):
                log.info(f"CTL COM port found: {i.device}")
                return i.device
            close(inst)
        except Exception as e:
            pass
    log.error('CTL COM port not found')
    return False

def check_com(ser: serial.Serial):
    try:
        cmd = b"IDN?\r\n"
        ser.reset_input_buffer()

        ser.write(cmd)

        time.sleep(0.1)
        data_str = ser.read_all()
        log.debug(f'data: {data_str}')
        data_str = data_str.decode('utf-8')
        log.debug(f'inst name: {data_str}')
        if COM_SYMBOL in data_str:
            return True
        else:
            return False
    except Exception as e:
        return False

def init():
    com = search_com()
    if com:
        try:
            ser = serial.Serial(com)
        except Exception as e:
            log.error("com not found: " + str(e))
            return False
        return ser
    else:
        return False
    
def close(ser: serial.Serial):
    try:
        ser.close()
    except Exception as e:
        log.error('close failed: ' + str(e))
    finally:
        log.info('closed')

def send_hex(ser: serial.Serial, hex_str: list):

    try:
        ser.write(bytes(hex_str))
        hex_string_readable = ', '.join(format(x, '02x') for x in hex_str)
        log.debug(f'hex sent: {hex_string_readable}')
    except Exception as e:
        log.error(f'Send failed: {str(e)}')

# PC --- "55 AA 00 10 [指令长度 1 byte][数据块ID 2 byte][数据帧数 2 byte][COL数 1 byte][ROW数 1 byte][帧间隔时间 2 byte][校验符 2 byte]" ---> 设备

def ready_info_string(data_block_id, data_frame_num, col_num, row_num, frame_interval_time):
    # 55 AA 00 10 00 01 00 01 01 01 00 00 00 00
    hex_str = [0x55, 0xAA, 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,  0x0D, 0x0A]
    hex_str[4] = 10
    hex_str[5] = data_block_id >> 8
    hex_str[6] = data_block_id & 0xFF
    hex_str[7] = data_frame_num >> 8
    hex_str[8] = data_frame_num & 0xFF
    hex_str[9] = col_num
    hex_str[10] = row_num
    hex_str[11] = frame_interval_time >> 8
    hex_str[12] = frame_interval_time & 0xFF
    hex_str[13] = 0x00
    hex_str[14] = 0x00
    hex_str[15] = 0x0D
    hex_str[16] = 0x0A

    return hex_str

def send_block_info(ser, data_block_id, data_frame_num, col_num, row_num, frame_interval_time):
    data_info = ready_info_string(data_block_id, data_frame_num, col_num, row_num, frame_interval_time)
    ser.reset_input_buffer()
    send_hex(ser, data_info)
    time.sleep(0.1)
    echo = ser.read_all().decode('utf-8')
    if "OK" in echo:
        log.debug("send block info success")
    else:
        log.error("send block info failed")

### 发送数据

# PC --- "55 AA 00 11 [指令长度 1 byte][数据块ID 2 byte][数据帧 序号 2 byte][COL 序号 1 byte][ROW的RGB数据 (1至ROW数)x3 byte][校验符 2 byte]" ---> 设备
# PC <--- "OK\r\n" --- 设备

def send_col_data(ser, data_block_id, frame_index, col_index, row_cnt, rgb_data):
    # 55 AA 00 11 01 00 01 01 {RR GG BB}*row_cnt 00 00

    hex_str = [0x55, 0xAA, 0x00, 0x11, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00]
    hex_str[5] = data_block_id >> 8
    hex_str[6] = data_block_id & 0xFF
    hex_str[7] = frame_index >> 8
    hex_str[8] = frame_index & 0xFF
    hex_str[9] = col_index
    
    for i in range(row_cnt):
        hex_str.append(rgb_data[i][0])
        hex_str.append(rgb_data[i][1])
        hex_str.append(rgb_data[i][2])

    hex_str.append(0x00)
    hex_str.append(0x00)
    hex_str.append(0x0D)
    hex_str.append(0x0A)

    ser.reset_input_buffer()
    send_hex(ser, hex_str)
    time.sleep(0.2)
    echo = ser.read_all().decode('utf-8')
    if "OK" in echo:
        log.debug("send block data success")
    else:
        log.error("send block data failed")

# PC --- "55 AA 00 12 [指令长度 1 byte][数据块ID 2 byte][数据块校验和 2 byte][循环模式 1 byte][显示模式 1 byte][校验符 2 byte]" ---> 设备

def send_transfer_finish_command(ser, data_block_id, data_block_checksum, loop_mode, display_mode):
    # 55 AA 00 12 00 01 00 01 00 00
    hex_str = [0x55, 0xAA, 0x00, 0x12, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00]
    hex_str[5] = data_block_id >> 8
    hex_str[6] = data_block_id & 0xFF
    hex_str[7] = data_block_checksum >> 8
    hex_str[8] = data_block_checksum & 0xFF

    if loop_mode == "CYCLE_ONCE":
        hex_str[9] = 0x00
    elif loop_mode == "CYCLE_LOOP":
        hex_str[9] = 0x01
    else:
        hex_str[9] = 0x00

    if display_mode == "PLAY_NOW":
        hex_str[10] = 0x00
    elif display_mode == "PLAY_QUEUE":
        hex_str[10] = 0x01
    else:
        hex_str[10] = 0x01

    hex_str.append(0x00)
    hex_str.append(0x00)
    hex_str.append(0x0D)
    hex_str.append(0x0A)

    ser.reset_input_buffer()
    send_hex(ser, hex_str)
    time.sleep(0.2)
    echo = ser.read_all().decode('utf-8')
    if "OK" in echo:
        log.debug("send transfer finish success")
    else:
        log.error("send transfer finish failed")
    


if __name__ == '__main__':
    
    # ser = init()
    ser = serial.Serial('COM7', 115200)
    if ser:
        
        send_block_info(ser, 1234, 11, 12, 13, 14)
        send_col_data(ser, 
                        data_block_id=1234,
                        frame_index=6,
                        col_index=8,
                        row_cnt=3,
                        rgb_data=[[1, 2, 3], [4, 11, 6], [7, 8, 9]])
        
        send_transfer_finish_command(ser, 1234, 5678, "CYCLE_ONCE", "PLAY_NOW")

        close(ser)
    else:
        log.error("init failed")